package com.comleader.operator.service.strategy;

import static cn.hutool.core.util.CharsetUtil.CHARSET_GBK;
import static com.comleader.operator.common.constant.Constant.*;
import static com.comleader.operator.common.constant.DictConstant.SMS_IDCARD_TYPE;
import static com.comleader.operator.common.enums.InspectStatusEnum.FAILED;
import static com.comleader.operator.common.enums.OperatorInspectTypeEnum.SMS_PORT;
import static com.comleader.operator.common.enums.StatisticsTypeEnum.NONCOMPLIANT;
import static com.comleader.operator.common.enums.StatisticsTypeEnum.UNREGISTER;
import static com.comleader.operator.common.util.DesensitizeEntityUtil.desensitizeSmsPort;
import static com.comleader.operator.validate.CommonValidator.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;

import javax.annotation.PostConstruct;

import org.springframework.stereotype.Service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.comleader.operator.common.constant.GlobalVariables;
import com.comleader.operator.common.util.ExcelUtil;
import com.comleader.operator.common.util.OperatorObjectUtil;
import com.comleader.operator.core.exception.BusinessException;
import com.comleader.operator.pojo.dto.SmsPortStatDTO;
import com.comleader.operator.pojo.entity.InspectRecordEntity;
import com.comleader.operator.pojo.entity.InspectReportEntity;
import com.comleader.operator.pojo.entity.SmsPortEntity;
import com.comleader.operator.service.InspectRecordService;
import com.comleader.operator.service.InspectReportService;
import com.comleader.operator.service.OperatorInspectService;
import com.comleader.operator.validate.SmsPortValidator;
import com.comleader.operator.validate.ValidateRule;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @Dscription
 * @Author 连新景
 * @Date 2024/07/29 16:49
 */
@AllArgsConstructor
@Slf4j
@Service
public class SmsPortInspect implements InspectStartegy {

    private final InspectRecordService inspectRecordService;

    private final InspectReportService inspectReportService;

    private final OperatorInspectService operatorInspectService;

    private static final List<ValidateRule<SmsPortEntity>> ruleList = new ArrayList<>();

    @PostConstruct
    public void init() {
        ruleList.add(new ValidateRule<>("501", "格式异常数据", SmsPortValidator::isFormatException, null));
        // 字段异常单独校验，因为缺失字段无法映射到对象上
        ruleList.add(new ValidateRule<>("503", "企业信息未登记", data -> isUnregister(data.getCompanyName(), data.getCompanyUsci()), UNREGISTER));
        ruleList.add(new ValidateRule<>("504", "法人信息未登记",
            data -> isUnregister(data.getLegalPersonName(), data.getLegalPersonIdType(), data.getLegalPersonIdNumber()), UNREGISTER));
        ruleList.add(new ValidateRule<>("505", "法人证件类型校验不合规", data -> isIdTypeNoncompliant(SMS_IDCARD_TYPE, data.getLegalPersonIdType()), NONCOMPLIANT));
        ruleList.add(new ValidateRule<>("506", "法人姓名校验不合规", data -> isNameNoncompliant(data.getLegalPersonName()), NONCOMPLIANT));
        ruleList.add(
            new ValidateRule<>("507", "法人证件号码校验不合规", data -> isIdNumberNoncompliant(data.getLegalPersonIdType(), data.getLegalPersonIdNumber()), NONCOMPLIANT));
        ruleList.add(new ValidateRule<>("508", "法人姓名&法人证件号码校验不合规",
            data -> isNameNoncompliant(data.getLegalPersonName()) && isIdNumberNoncompliant(data.getLegalPersonIdType(), data.getLegalPersonIdNumber()), null));
        ruleList
            .add(new ValidateRule<>("509", "经办人信息未登记", data -> isUnregister(data.getAgentName(), data.getAgentIdType(), data.getAgentIdNumber()), UNREGISTER));
        ruleList.add(new ValidateRule<>("510", "经办人证件类型校验不合规", data -> isIdTypeNoncompliant(SMS_IDCARD_TYPE, data.getAgentIdType()), NONCOMPLIANT));
        ruleList.add(new ValidateRule<>("511", "经办人姓名校验不合规", data -> isNameNoncompliant(data.getAgentName()), NONCOMPLIANT));
        ruleList.add(new ValidateRule<>("512", "经办人证件号码校验不合规", data -> isIdNumberNoncompliant(data.getAgentIdType(), data.getAgentIdNumber()), NONCOMPLIANT));
        ruleList.add(new ValidateRule<>("513", "经办人姓名&证件号码校验不合规",
            data -> isNameNoncompliant(data.getAgentName()) && isIdNumberNoncompliant(data.getAgentIdType(), data.getAgentIdNumber()), null));

    }

    @Override
    public void inspect(InspectRecordEntity inspectRecord, String importFilePath) {
        int inspectRecordId = inspectRecord.getId();
        String reportFileDir = inspectRecord.getReportFileDir();
        try {
            List<ValidateRule<SmsPortEntity>> enableRules = listEnableValidateRule(ruleList, SMS_PORT);
            BufferedReader reader = null;
            String line;
            List<String> filedList;
            List<String> fieldExceptionList = new ArrayList<>();
            Map<String, List<SmsPortEntity>> codeEntityMap = new TreeMap<>();
            // 总量
            int totalNum = 0;
            SmsPortStatDTO smsPortStatDTO = new SmsPortStatDTO();
            try {
                reader = FileUtil.getReader(new File(importFilePath), CHARSET_GBK);
                while (true) {
                    line = reader.readLine();
                    if (StrUtil.isBlank(line)) {
                        break;
                    }
                    totalNum++;
                    filedList = StrUtil.split(line, "||");
                    if (filedList.size() != SMS_PORT_FIELD_NUM) {
                        fieldExceptionList.add(line);
                        continue;
                    }
                    // 未登记标识
                    boolean unregisterFlag = false;
                    // 不合规标识
                    boolean noncomplaianceFlag = false;
                    // 合规标识
                    boolean complaianceFlag = true;
                    SmsPortEntity smsPort = OperatorObjectUtil.createObj(filedList, SmsPortEntity.class);
                    // 公共校验规则
                    for (ValidateRule<SmsPortEntity> validateRule : enableRules) {
                        if (validateRule.getValidate().test(smsPort)) {
                            // 只要有一个匹配上，那就不是合规的
                            complaianceFlag = false;
                            codeEntityMap.computeIfAbsent(validateRule.getRuleCode(), key -> ListUtil.list(false)).add(smsPort);
                            if (UNREGISTER == validateRule.getType()) {
                                unregisterFlag = true;
                            } else if (NONCOMPLIANT == validateRule.getType()) {
                                noncomplaianceFlag = true;
                            }
                        }
                    }
                    // 只要是未登记或者不合规，那么肯定不属于合规了，因为合规的要求是该类型所有的不合规项都匹配不上
                    if (unregisterFlag || noncomplaianceFlag) {
                        if (unregisterFlag) {
                            smsPortStatDTO.getUnregisterList().add(smsPort);
                        }
                        if (noncomplaianceFlag) {
                            smsPortStatDTO.getNoncompliantList().add(smsPort);
                        }
                    } else if (complaianceFlag) {
                        smsPortStatDTO.getCompliantList().add(smsPort);
                    }
                }
            } catch (IOException exception) {
                log.error("短信端口检查失败，读取文件内容异常", exception);
                operatorInspectService.deleteImportData(SMS_PORT.getType(), inspectRecord.getImportRecordId());
                FileUtil.del(new File(reportFileDir));
                inspectRecordService.updateStatusAndFinishTime(inspectRecordId, FAILED);
                throw exception;
            } finally {
                IoUtil.close(reader);
            }
            // 启用的校验规则code
            List<String> enableCodeList = GlobalVariables.getTypeCodeMap().get(SMS_PORT.getType());
            Map<String, String> codeNameMap = GlobalVariables.getCodeNameMap();
            List<InspectReportEntity> inspectReportList = new ArrayList<>();
            // 统计map
            Map<String, Integer> statisticsMap = new LinkedHashMap<>();
            statisticsMap.put("检查总量", totalNum);
            if (enableCodeList.contains(SMS_PORT_FORMAT_EXCEPTION_CODE)) {
                statisticsMap.put("格式异常数据量",
                    !codeEntityMap.containsKey(SMS_PORT_FORMAT_EXCEPTION_CODE) ? 0 : codeEntityMap.get(SMS_PORT_FORMAT_EXCEPTION_CODE).size());
            }
            // 是否校验字段异常
            if (enableCodeList.contains(SMS_PORT_FIELD_EXCEPTION_CODE)) {
                statisticsMap.put("字段异常数据量", fieldExceptionList.size());
                if (CollUtil.isNotEmpty(fieldExceptionList)) {
                    String fileName = codeNameMap.getOrDefault(SMS_PORT_FIELD_EXCEPTION_CODE, "字段异常数据") + ".txt";
                    // 完整路径为./reportFile/yyyyMMddHHmmss/字段异常数据.txt
                    FileUtil.writeUtf8Lines(fieldExceptionList, new File(reportFileDir + fileName));
                    InspectReportEntity inspectReportEntity =
                        InspectReportEntity.builder().fileName(fileName).recordId(inspectRecordId).generateTime(LocalDateTime.now()).build();
                    inspectReportList.add(inspectReportEntity);
                }
            }
            statisticsMap.put("未登记总量", smsPortStatDTO.getUnregisterList().size());
            statisticsMap.put("不合规总量", smsPortStatDTO.getNoncompliantList().size());
            statisticsMap.put("全量合规量", smsPortStatDTO.getCompliantList().size());
            // 遍历启用的校验规则
            enableCodeList.forEach(ruleCode -> {
                // 格式异常、字段异常上边已经处理过了
                if (SMS_PORT_FORMAT_EXCEPTION_CODE.equals(ruleCode) || SMS_PORT_FIELD_EXCEPTION_CODE.equals(ruleCode)) {
                    return;
                }
                // 如果codeEntityMap没有该校验规则code，表示该校验规则虽然启用了但是没有符合条件的数据，
                if (!codeEntityMap.containsKey(ruleCode)) {
                    statisticsMap.put(codeNameMap.get(ruleCode), 0);
                } else {
                    statisticsMap.put(codeNameMap.get(ruleCode), codeEntityMap.get(ruleCode).size());
                }
            });
            genreateFixedTermsExcel(statisticsMap, reportFileDir, inspectRecordId, inspectReportList, smsPortStatDTO);
            // 开始循环生成excel文件
            codeEntityMap.forEach((key, value) -> {
                if (CollectionUtil.isEmpty(value)) {
                    return;
                }
                // 完整路径为./reportFile/yyyyMMddHHmmss/校验规则名称.xlsx
                String fileName = codeNameMap.get(key) + ExcelTypeEnum.XLSX.getValue();
                String fullPath = reportFileDir + fileName;
                desensitizeSmsPort(value);
                ExcelUtil.writeMultipleSheet(fullPath, SmsPortEntity.class, codeNameMap.get(key), value);
                inspectReportList.add(InspectReportEntity.builder().fileName(fileName).recordId(inspectRecordId).generateTime(LocalDateTime.now()).build());
            });
            inspectReportService.saveBatch(inspectReportList);
        } catch (Exception exception) {
            log.error("短信端口检查失败", exception);
            operatorInspectService.deleteImportData(SMS_PORT.getType(), inspectRecord.getImportRecordId());
            FileUtil.del(new File(reportFileDir));
            inspectRecordService.updateStatusAndFinishTime(inspectRecordId, FAILED);
            throw new BusinessException(exception);
        }
    }

    /**
     * @Description 生成固定项excel文件
     * @Auhtor 连新景
     * @Date 2024/08/13 17:41
     * @param statisticsMap
     * @param reportFileDir
     * @param inspectRecordId
     * @param inspectReportList
     * @param smsPortStatDTO
     */
    private void genreateFixedTermsExcel(Map<String, Integer> statisticsMap, String reportFileDir, Integer inspectRecordId,
        List<InspectReportEntity> inspectReportList, SmsPortStatDTO smsPortStatDTO) {
        // 生成检查统计表
        List<List<Object>> statisticsList = new ArrayList<>(statisticsMap.size());
        statisticsMap.forEach((key, value) -> {
            List<Object> data = new ArrayList<>(2);
            data.add(key);
            data.add(value);
            statisticsList.add(data);
        });
        EasyExcel.write(reportFileDir + "检查统计表.xlsx").registerWriteHandler(new InspectStatColumnWidthStyleStrategy()).sheet("检查统计表").doWrite(statisticsList);
        InspectReportEntity inspectReportEntity =
            InspectReportEntity.builder().fileName("检查统计表.xlsx").recordId(inspectRecordId).generateTime(LocalDateTime.now()).build();
        inspectReportList.add(inspectReportEntity);
        // 生成固定明细项
        if (CollectionUtil.isNotEmpty(smsPortStatDTO.getUnregisterList())) {
            desensitizeSmsPort(smsPortStatDTO.getUnregisterList());
            ExcelUtil.writeMultipleSheet(reportFileDir + "未登记数据.xlsx", SmsPortEntity.class, "未登记数据", smsPortStatDTO.getUnregisterList());
            inspectReportEntity = InspectReportEntity.builder().fileName("未登记数据.xlsx").recordId(inspectRecordId).generateTime(LocalDateTime.now()).build();
            inspectReportList.add(inspectReportEntity);
        }
        if (CollectionUtil.isNotEmpty(smsPortStatDTO.getNoncompliantList())) {
            desensitizeSmsPort(smsPortStatDTO.getNoncompliantList());
            ExcelUtil.writeMultipleSheet(reportFileDir + "不合规数据.xlsx", SmsPortEntity.class, "不合规数据", smsPortStatDTO.getNoncompliantList());
            inspectReportEntity = InspectReportEntity.builder().fileName("不合规数据.xlsx").recordId(inspectRecordId).generateTime(LocalDateTime.now()).build();
            inspectReportList.add(inspectReportEntity);
        }
        if (CollectionUtil.isNotEmpty(smsPortStatDTO.getCompliantList())) {
            desensitizeSmsPort(smsPortStatDTO.getCompliantList());
            ExcelUtil.writeMultipleSheet(reportFileDir + "全量合规数据.xlsx", SmsPortEntity.class, "全量合规数据", smsPortStatDTO.getCompliantList());
            inspectReportEntity = InspectReportEntity.builder().fileName("全量合规数据.xlsx").recordId(inspectRecordId).generateTime(LocalDateTime.now()).build();
            inspectReportList.add(inspectReportEntity);
        }

    }
}
